package com.hotel.BL;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.EntityName;
import com.Util;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;

import data.Charge;
import data.Counter;
import data.General;
import data.Reservation;
import data.WaitToUpdate;
import data.passingData.AccDetailsData;
import data.passingData.CounterData;
import data.passingData.NightAuditReportResData;
import data.passingData.SummaryNightAuditData;

public class NightAuditLogic extends AbstractBL {
	
	public NightAuditLogic(HttpServletRequest req,HttpServletResponse resp){
		this.req = req;
		this.resp = resp;
	}
	
	public void ChangeIsTempCharge(long hotelId,Date date) throws EntityNotFoundException{
		ChargeLogic chargeLogic = new ChargeLogic(req,resp);
		List<Charge> chargelist = chargeLogic.GetCharge(hotelId, date);
		for(int i =0;i<chargelist.size();i++){
			chargelist.get(i).setTemp(false);
			chargeLogic.UpdateCharge(chargelist.get(i));
		new FolioLogic(req,resp).Lockfolio(chargelist.get(i).getFolioId());
		}
	}
	
	public void SaveSerialize(long hotelId) throws ClassNotFoundException, IOException, EntityNotFoundException{
		List<WaitToUpdate> list = GetWaitToUpdate(hotelId);
		GenericLogic gLogic = new GenericLogic(req,resp);
		for(int i=0;i<list.size();i++){
			Entity e = list.get(i).getEntity();
			Text data = (Text) e.getProperty("data");
			@SuppressWarnings("rawtypes")
			Class classString = Class.forName((String)e.getProperty("class"));
			String className = (String)e.getProperty("class");
			Object o = com.Util.Deserialize(data.getValue(),classString);
			if(className == "data.General"){
				gLogic.UpdateGenerlic((General) o);
			}
		}
		removeWaitToUpdate(list);
	}
	
	public List<WaitToUpdate> GetWaitToUpdate(long hotelId)
	{
		Query q = new Query(EntityName.WaitToUpdate);
		
		Filter filter = new FilterPredicate("avaliable",FilterOperator.EQUAL, hotelId);

		q.setFilter(filter);
		return GetList(datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()));
	}
	
	public List<WaitToUpdate> GetList(List<Entity> entities) {
		List<WaitToUpdate> list = new ArrayList<WaitToUpdate>();
		if(entities!=null)
		for (Entity e : entities) {
			WaitToUpdate a = new WaitToUpdate();
			a.setEntity(e);
			list.add(a);
		}
		return list;
	}
	
	public void removeWaitToUpdate(List<WaitToUpdate> list){
		for(WaitToUpdate item : list)
		{
			Key k = KeyFactory.createKey(EntityName.WaitToUpdate, item.getId());
			datastore.delete(k);
		}
	}
	
	public void Keeping(long hotelId,Object obj,String typename) throws IOException{
		String rs = com.Util.Serialize(obj);
		Text text = new Text(rs);
		String type = typename;
		
		WaitToUpdate wtu = new WaitToUpdate();
			wtu.setData(text);
			wtu.setHotelId(hotelId);
			wtu.setType(type);
		
			datastore.put(wtu.getEntity());
	}
	
	public void ChangeCleaningStatusAllRoom(long statusId,List<Reservation> room) throws EntityNotFoundException{
		for(int i=0;i<room.size();i++){
			new RoomLogic(req,resp).ChangeCleaningStatus(room.get(0).getRoom().getId(), statusId);
		}
	}

	public List<CounterData> CounterToDay(long hotelId){
		List<Counter> counterlist = new CounterLogic().GetAllCounter(hotelId);
		
		List<CounterData> cList = new ArrayList<CounterData>();
		///////////////////////////////
		//TODO: swap code
//		Date dateFrom = Util.addDays(new GenericLogic().ServerDateTime(hotelId), -1);
//		Date dateTo = new GenericLogic().ServerDateTime(hotelId);
		Date dateFrom = new GenericLogic(req,resp).ServerDateTime(hotelId);
		Date dateTo = Util.addDays(dateFrom, 1);
		///////////////////////////////////////////////////
		dateFrom = ChangeToNightAuditTime(hotelId,dateFrom);
		dateTo = ChangeToNightAuditTime(hotelId,dateTo);
		
		for(int i=0;i<counterlist.size();i++){
			long counterid = counterlist.get(i).getId();
			CounterData cData = new CounterData();
			cData.setCounterId(counterid);
			cData.setCounterName(counterlist.get(i).getTitle());
			if(counterlist.get(i).getLastUserString() != null){
				cData.setLastUserId(Long.parseLong(counterlist.get(i).getLastUserString()));
				cData.setLastUserName(counterlist.get(i).getLastUser().getFirstName()+" "+counterlist.get(i).getLastUser().getLastName());
			}
			long IdCash = new MasterCreator().GetPayModeMaster("Cash").getId();
			long IdCheque = new MasterCreator().GetPayModeMaster("Cheque").getId();
			long IdCC = new MasterCreator().GetPayModeMaster("Credit Card").getId();
			PaymentLogic pLogic = new PaymentLogic(req,resp);
			double cash = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCash);
			double cheque = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCheque);
			double cc = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCC);
			cData.setCash(cash);
			cData.setCheque(cheque+cc);
			cList.add(cData);
		}
		return cList;
	}
	
	public List<NightAuditReportResData> GetGuestCheckInToDay(List<Reservation> resCheckIn){
		List<NightAuditReportResData> list = new ArrayList<NightAuditReportResData>();
		for(int i=0;i< resCheckIn.size();i++){
			int s = 1;
			//TODO: ��� �礨ӹǹᢡ ��͹���Թ �������� ź�͡����
			if(resCheckIn.get(i).getAdditionalGuest() != null){
				s =resCheckIn.get(i).getAdditionalGuest().size();
			}
			for(int x=0;x<s;x++){
				//TODO: ��� �礨ӹǹᢡ ��͹���Թ �������� ź�͡����
				if(resCheckIn.get(i).getAdditionalGuest() == null){
					list.add(null);
				}else{
					NightAuditReportResData data = new NightAuditReportResData();
					data.setCheckInTime(resCheckIn.get(i).getDateCheckin());
					data.setCheckOutTime(Util.addDays(resCheckIn.get(i).getDateCheckin(), resCheckIn.get(i).getSpan()));
					double pay = 0;
					if(resCheckIn.get(i).getPaymentList().size() != 0){
						for(int y=0;y<resCheckIn.get(i).getPaymentList().size();y++){
							pay += resCheckIn.get(i).getPaymentList().get(y).getAmount();
						}
					}
					data.setPay(pay);
					data.setResId(resCheckIn.get(i).getId());
					data.setRoomName(resCheckIn.get(i).getRoom().getRoomName());
					data.setRoomtypeName(resCheckIn.get(i).getRoomType().getShortName());
					data.setUserId(resCheckIn.get(i).getAdditionalGuest().get(x).getId());
					
					String userName = resCheckIn.get(i).getAdditionalGuest().get(x).getFirstName();
					if(!resCheckIn.get(i).getAdditionalGuest().get(x).getSalutationString().equals("0") &&
							resCheckIn.get(i).getAdditionalGuest().get(x).getSalutationString() != null	){
						userName = resCheckIn.get(i).getAdditionalGuest().get(x).getSalutation().getTitle()
								+userName;
					}
					if(resCheckIn.get(i).getAdditionalGuest().get(x).getLastName() != null){
						userName += " " + resCheckIn.get(i).getAdditionalGuest().get(x).getLastName();
					}
					data.setUserName(userName);
					list.add(data);
				}
			}
		}
		return list;
	}
	
	public SummaryNightAuditData GetSummaryData(long hotelId){
		ReservationLogic resLogic = new ReservationLogic(req,resp);
		ChargeLogic chargeLogic = new ChargeLogic(req,resp);
		List<Reservation> res = resLogic.GetStayToday(hotelId);
		int occupiedroom = res.size();
		int occupiedguest = 0;
		for(int i=0;i<res.size();i++){
			occupiedguest += res.get(i).getAdditionalGuest().size();
		}
		int allroom = new RoomLogic(req,resp).GetAllRoom(hotelId).size();
		int availableroom = allroom-occupiedguest;
		List<Reservation> checkintodaylist = resLogic.GetCheckinToday(hotelId);
		int checkintoday = checkintodaylist.size();
		int checkintodayguest = 0;
		for(int i=0;i<checkintodaylist.size();i++){
			checkintodayguest += checkintodaylist.get(i).getAdditionalGuest().size();
		}
		List<Charge> noshowtodaylist =  chargeLogic.GetNoShowToDay(hotelId);
		List<Reservation> canceltodaylist = resLogic.GetCancelToDay(hotelId);
		int checkouttoday = resLogic.GetCheckinToday(hotelId).size();
		int noshowtoday = noshowtodaylist.size();
		int canceltoday = canceltodaylist.size();
		int earlycheckouttoday = chargeLogic.GetEarlyCheckOutToDay(hotelId).size();
		double roomrevenue = chargeLogic.GetRoomChargeRevenue(hotelId);
		double noshowrevenue = chargeLogic.GetRevenue(noshowtodaylist);
		double cancellationrevenue = chargeLogic.GetRevenue(hotelId);
		
		SummaryNightAuditData sum = new SummaryNightAuditData();
		sum.setOccupiedRooms_Rooms(occupiedroom);
		sum.setOccupiedRooms_Guest(occupiedguest);
		sum.setAvaliableRooms(availableroom);
		sum.setCheckIns_Rooms(checkintoday);
		sum.setCheckIns_Guest(checkintodayguest);
		sum.setCheckOuts(checkouttoday);
		sum.setNoShows(noshowtoday);
		sum.setCancellations(canceltoday);
		sum.setOccupiedRoomsMarked(occupiedroom);
		sum.setVacantRoomsMarked(availableroom - earlycheckouttoday);
		sum.setCheckedOutRoomsMarked(earlycheckouttoday);
		sum.setRoomRevenue(roomrevenue);
		sum.setNoShowRevenue(noshowrevenue);
		sum.setCancellationRevenue(cancellationrevenue);
		//TODO : 
		sum.setWalkIn(roomrevenue);
		sum.setCoporate(0);
		sum.setAgnet(0);
		sum.setWeb(0);
		
		return sum;
	}
	
	public List<AccDetailsData> GetAccDetails(long hotelId){
		List<Counter> counterlist = new CounterLogic().GetAllCounter(hotelId);
		
		List<AccDetailsData> list = new ArrayList<AccDetailsData>();
		///////////////////////////////
		//TODO: swap code
//		Date dateFrom = Util.addDays(new GenericLogic().ServerDateTime(hotelId), -1);
//		Date dateTo = new GenericLogic().ServerDateTime(hotelId);
		Date dateFrom = new GenericLogic(req,resp).ServerDateTime(hotelId);
		Date dateTo = Util.addDays(dateFrom, 1);
		///////////////////////////////////////////////////
		dateFrom = ChangeToNightAuditTime(hotelId,dateFrom);
		dateTo = ChangeToNightAuditTime(hotelId,dateTo);
		
		for(int i=0;i<counterlist.size();i++){
			long counterid = counterlist.get(i).getId();
			AccDetailsData cData = new AccDetailsData();
			cData.setCounterName(counterlist.get(i).getTitle());
			
			long IdCash = new MasterCreator().GetPayModeMaster("Cash").getId();
			long IdCheque = new MasterCreator().GetPayModeMaster("Cheque").getId();
			long IdCC = new MasterCreator().GetPayModeMaster("Credit Card").getId();
			PaymentLogic pLogic = new PaymentLogic(req,resp);
			double cash = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCash);
			double cheque = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCheque);
			double cc = pLogic.GetPayMentInCounterByPayMode(hotelId,counterid
					,dateFrom,dateTo,IdCC);
			double wCash = 0;
			double wCC = 0;
			double wCheque = 0;
			cData.setBalance((cash+cheque+cc)-(wCash+wCC+wCheque));
			cData.setRevenueCash(cash);
			cData.setRevenueCC(cheque+cc);
			cData.setWithdrawalsCash(wCash);
			cData.setWithdrawalsCC(wCC+wCheque);
			list.add(cData);
		}
		return list;
	}
	
	public Date ChangeToNightAuditTime(long hotelId,Date date){
		// ���� date ��� ���Ѻ������ ���ҷ�������� night audit
		
		Date nightAuditTime;
		HttpSession session = req.getSession(true);
		if(session.getAttribute("NightAuditTime") != null)
			nightAuditTime = (Date) session.getAttribute("NightAuditTime");
		else{
			General g = new GenericLogic(req,resp).GetGeneral(hotelId);
			nightAuditTime = g.getNightAuditTime();
		}
		Calendar cal = Calendar.getInstance();
		Calendar calNightAudit = Calendar.getInstance();
		calNightAudit.setTime(nightAuditTime);
		int hour = calNightAudit.get(Calendar.HOUR_OF_DAY);
		int minute = calNightAudit.get(Calendar.MINUTE);
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY,hour);
		cal.set(Calendar.MINUTE,minute);
		cal.set(Calendar.SECOND,0);
		cal.set(Calendar.MILLISECOND,0);
		return cal.getTime();
	}
}
